package com.naiterui.ehp.bs.b2c.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.base.Joiner;
import com.google.common.collect.Sets;
import com.naiterui.ehp.bp.bo.patient.MsgMaPublishParamBO;
import com.naiterui.ehp.bp.support.exception.BaseException;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.context.SpringContextHolder;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bs.b2c.common.bo.inventory.StockQuantityBO;
import com.naiterui.ehp.bs.b2c.inventory.exception.StockServiceException;
import com.naiterui.ehp.bs.b2c.inventory.service.IStockService;
import com.naiterui.ehp.bs.b2c.order.bo.LogisticsInfoBO;
import com.naiterui.ehp.bs.b2c.order.bo.LogisticsTraceLogBO;
import com.naiterui.ehp.bs.b2c.order.constants.OrderConstants;
import com.naiterui.ehp.bs.b2c.order.constants.OrderStatusEnum;
import com.naiterui.ehp.bs.b2c.order.entity.LogisticsOrder;
import com.naiterui.ehp.bs.b2c.order.entity.LogisticsRouteOrder;
import com.naiterui.ehp.bs.b2c.order.entity.LogisticseCompany;
import com.naiterui.ehp.bs.b2c.order.entity.Order;
import com.naiterui.ehp.bs.b2c.order.entity.OrderItem;
import com.naiterui.ehp.bs.b2c.order.entity.OrderUser;
import com.naiterui.ehp.bs.b2c.order.fegin.IPatientFeignClient;
import com.naiterui.ehp.bs.b2c.order.fegin.IRecommendFeignClient;
import com.naiterui.ehp.bs.b2c.order.repository.ILogisticsOrderRepository;
import com.naiterui.ehp.bs.b2c.order.repository.ILogisticsRouteOrderRepository;
import com.naiterui.ehp.bs.b2c.order.repository.ILogisticseCompanyRepository;
import com.naiterui.ehp.bs.b2c.order.repository.IOrderItemRepository;
import com.naiterui.ehp.bs.b2c.order.repository.IOrderRepository;
import com.naiterui.ehp.bs.b2c.order.repository.IOrderUserRepository;
import com.naiterui.ehp.bs.b2c.order.service.ILogisticsTraceService;
import com.naiterui.ehp.bs.b2c.order.service.IOrderJobService;
import com.naiterui.ehp.bs.b2c.order.service.IOrderLogService;
import com.naiterui.ehp.bs.b2c.order.service.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrderJobServiceImpl implements IOrderJobService {

    @Autowired
    private IOrderService orderService;
    @Autowired
    private IStockService stockService;
    @Autowired
    private IOrderLogService orderLogService;
    @Autowired
    private IOrderRepository orderRepository;
    @Autowired
    private IOrderItemRepository orderItemRepository;
    @Autowired
    private ILogisticsOrderRepository logisticsOrderRepository;
    @Autowired
    private ILogisticseCompanyRepository logisticseCompanyRepository;
    @Autowired
    private ILogisticsRouteOrderRepository logisticsRouteOrderRepository;
    @Autowired
    private IRecommendFeignClient recommendFeignClient;
    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private IOrderUserRepository orderUserRepository;

    @Autowired
    private IPatientFeignClient patientFeignClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void systemAutoCancelOrder() {
        Date changedAt = DateUtil.plus(new Date(), -OrderConstants.AUTO_CLOSE_ORDER_MINUTE, ChronoUnit.MINUTES);
        // 查询超时未支付订单列表
        List<Order> orders = this.orderRepository.findByOrderStatusAndChangedAtBefore(OrderStatusEnum.PENDING.getStatus(), changedAt);
        // 关单
        orders.forEach(order -> {
            try {
                this.orderService.cancel(order.getId());
                log.info("订单自动取消成功 orderId:{}", order.getId());
            } catch (BaseException e) {
                log.warn("批量自动关单失败 userId:{}, orderId:{}, msg:{}", order.getUserId(), order.getId(), e.getMessage());
            } catch (Exception e) {
                log.error("批量自动关单异常 userId:{}, orderId:{},", order.getUserId(), order.getId(), e);
            }
        });

    }

    @Override
    public void systemAutoWillWarehouse() {
        Set<Integer> orderStatusList = Sets.newHashSet(OrderStatusEnum.WAIT_AUDIT.getStatus(), OrderStatusEnum.AUDIT_COMPLETE.getStatus());
        List<Order> orders = this.orderRepository.findByOrderStatusInAndWarehouseIdNull(orderStatusList);
        orders.forEach(order -> this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(@NonNull TransactionStatus transactionStatus) {
                Long orderId = order.getId();
                try {
                    List<StockQuantityBO> pqs = OrderJobServiceImpl.this.buildStockQuantity(orderId);
                    Long warehouseId = OrderJobServiceImpl.this.stockService.preAssignWarehouse(orderId, order.getOrderSn(), pqs);
                    log.info("订单预分配仓库成功 orderId:{} warehouseId:{}", orderId, warehouseId);
                    order.setWarehouseId(warehouseId);
                    order.setChangedAt(new Date());
                    order.setChangedBy("system");
                    Integer orderStatus = order.getOrderStatus();
                    //增加日志
                    if (orderStatus.equals(OrderStatusEnum.WAIT_AUDIT.getStatus())) {
                        OrderJobServiceImpl.this.orderLogService.save(orderId, "预分配仓库完成，等待人工/系统审核。", orderStatus,
                                OrderStatusEnum.WAIT_AUDIT.getStatus(), "系统");
                    } else {
                        OrderJobServiceImpl.this.orderLogService.save(orderId, "预分配仓库完成，等待库房分配。", orderStatus,
                                OrderStatusEnum.AUDIT_COMPLETE.getStatus(), "系统");
                    }
                    OrderJobServiceImpl.this.orderRepository.update(order);
                } catch (StockServiceException e) {
                    log.error("系统预分配仓库失败 orderId:{} message:{}", order.getId(), e.getMessage());
                    transactionStatus.setRollbackOnly();
                } catch (Exception e) {
                    log.error("系统预分配仓库失败 orderId:{}", order.getId(), e);
                    transactionStatus.setRollbackOnly();
                }
            }
        }));
    }

    @Override
    public void systemAutoAuditOrder() {
        Date changedAt = DateUtil.plus(new Date(), -OrderConstants.AUTO_AUDIT_ORDER_MINUTE, ChronoUnit.MINUTES);
        List<Order> orders = this.orderRepository.findByOrderStatusAndChangedAtBefore(OrderStatusEnum.WAIT_AUDIT.getStatus(), changedAt);
        orders.forEach(order -> this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(@NonNull TransactionStatus transactionStatus) {
                try {
                    Integer beforeOrderStatus = order.getOrderStatus();
                    order.setOrderStatus(OrderStatusEnum.AUDIT_COMPLETE.getStatus());
                    order.setChangedAt(new Date());
                    order.setChangedBy("system");
                    OrderJobServiceImpl.this.orderRepository.update(order);
                    OrderJobServiceImpl.this.orderLogService.save(order.getId(), "人工/系统审核完成，等待库房分配。", beforeOrderStatus,
                            OrderStatusEnum.AUDIT_COMPLETE.getStatus(), "系统");
                    log.info("订单自动审核完成 orderId:{}", order.getId());
                } catch (Exception e) {
                    log.error("系统自动审核失败 orderId:{}", order.getId(), e);
                    transactionStatus.setRollbackOnly();
                }
            }
        }));
    }

    @Override
    public void systemAutoAssignedWarehouse() {
        List<Order> orders = this.orderRepository.findByOrderStatusAndWarehouseIdNotNull(OrderStatusEnum.AUDIT_COMPLETE.getStatus());
        orders.forEach(order -> this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(@NonNull TransactionStatus transactionStatus) {
                Long orderId = order.getId();
                try {
                    Long warehouseId = order.getWarehouseId();
                    List<StockQuantityBO> pqs = OrderJobServiceImpl.this.buildStockQuantity(orderId);
                    if (OrderJobServiceImpl.this.stockService.checkWarehouseSku(warehouseId, pqs)) {
                        OrderJobServiceImpl.this.stockService.assignWarehouse(orderId, order.getOrderSn(), warehouseId, pqs);
                        order.setOrderStatus(OrderStatusEnum.ASSIGNED_COMPLETE.getStatus());
                        order.setChangedAt(new Date());
                        order.setChangedBy("system");
                        OrderJobServiceImpl.this.orderRepository.update(order);
                        OrderJobServiceImpl.this.orderLogService.save(orderId, "库房分配完成，等待库房确认。", order.getOrderStatus(),
                                OrderStatusEnum.ASSIGNED_COMPLETE.getStatus(), "系统");
                        log.info("订单分配仓库成功 orderId:{}", orderId);
                    } else {
                        log.info("仓库中的商品不满足该订单，需要重新分配仓库。");
                        OrderJobServiceImpl.this.stockService.modifyWarehouse(orderId, order.getOrderSn(), warehouseId, null, pqs);
                        order.setWarehouseId(null);
                        order.setChangedAt(new Date());
                        order.setChangedBy("system");
                        OrderJobServiceImpl.this.orderRepository.update(order);
                        OrderJobServiceImpl.this.orderLogService.save(orderId, "仓库中的商品不满足该订单，需要重新分配仓库。", order.getOrderStatus(),
                                order.getOrderStatus(), "系统");
                    }
                } catch (StockServiceException e) {
                    log.error("订单分配仓库失败 orderId:{} message:{}", order.getId(), e.getMessage());
                    transactionStatus.setRollbackOnly();
                } catch (Exception e) {
                    log.error("系统自动分配库房失败 orderId:{}", orderId, e);
                    transactionStatus.setRollbackOnly();
                }
            }
        }));
    }

    @Override
    public void logistice() {
        List<Order> orders = this.orderRepository.findByOrderStatus(OrderStatusEnum.SHIPPING.getStatus());
        log.info("更新物流信息, 获取到需要处理订单数据 {}", JsonMapper.toJson(orders));
        List<LogisticseCompany> companyList = this.logisticseCompanyRepository.findAll();
        Map<Long, String> logisticsCompanyMap = companyList.stream().collect(Collectors.toMap(LogisticseCompany::getId, LogisticseCompany::getCode));

        Set<Long> orderIds = orders.stream().map(Order::getId).collect(Collectors.toSet());

        List<LogisticsOrder> logisticsOrderList = this.logisticsOrderRepository.findByOrderIdIn(orderIds);
        Map<Long, LogisticsOrder> logisticsOrderMap = logisticsOrderList.stream()
                .filter(f -> StringUtils.isNotEmpty(f.getDeliveryId()))
                .collect(Collectors.toMap(LogisticsOrder::getOrderId, Function.identity()));

        orders.forEach(order -> this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            public void doInTransactionWithoutResult(@NonNull TransactionStatus transactionStatus) {
                Long orderId = order.getId();
                try {
                    LogisticsOrder logisticsOrder = logisticsOrderMap.get(orderId);
                    if (logisticsOrder == null) {
                        return;
                    }
                    OrderUser orderUser = OrderJobServiceImpl.this.orderUserRepository.findByOrderIdAndType(orderId, OrderUser.ORDER_TYPE_SHOPPING);
                    String deliveryId = logisticsOrder.getDeliveryId();
                    log.info("更新物流信息，获取订单物流信息 orderId:{} deliveryId:{}", orderId, deliveryId);
                    Long logisticsCompanyId = logisticsOrder.getLogisticsCompanyId();
                    String logisticsCompanyCode = logisticsCompanyMap.get(logisticsCompanyId);
                    ILogisticsTraceService logisticsTraceService = OrderJobServiceImpl.this.getLogisticsTraceService(logisticsCompanyCode);
                    LogisticsInfoBO logisticsInfo = logisticsTraceService.getTrace(deliveryId, logisticsCompanyCode, orderUser.getPhone());
                    log.info("更新物流信息，获取订单物流信息 orderId:{}， deliveryId:{}，获取到的物流信息为 {}", orderId, deliveryId, JsonMapper.toJson(logisticsInfo));
                    Integer status = logisticsInfo.getStatus();
                    if (status == LogisticsInfoBO.STATUS_NONE) {
                        return;
                    }
                    if (status == LogisticsInfoBO.STATUS_RECEIVED) {
                        log.info("更新物流信息，订单签收逻辑处理 orderId:{}", orderId);
                        OrderJobServiceImpl.this.orderLogService.save(orderId, "物流已经签收。", order.getOrderStatus(),
                                OrderStatusEnum.RECEIVED.getStatus(), "系统");
                        order.setOrderStatus(OrderStatusEnum.RECEIVED.getStatus());
                        order.setChangedAt(new Date());
                        order.setChangedBy("system");
                        OrderJobServiceImpl.this.orderRepository.update(order);
                        List<OrderItem> orderItems = OrderJobServiceImpl.this.orderItemRepository.findAllByOrderId(orderId);
                        List<Long> recomIds = orderItems.stream()
                                .filter(orderItem -> orderItem.getRecomId() != null && orderItem.getRecomId() != 0)
                                .map(OrderItem::getRecomId)
                                .collect(Collectors.toList());
                        log.info("订单拖投，通知推荐,orderId:{} recomIds:{}", orderId, recomIds);
                        OrderJobServiceImpl.this.recommendFeignClient.orderCompleteNotify(orderId, order.getOrderSn(), Joiner.on(",").join(recomIds));
                        // 发送收货提醒
                        List<String> productNames = orderItems.stream().map(OrderItem::getProductName).collect(Collectors.toList());
                        Map<String, Object> paramsMap = new HashMap<>(6);
                        paramsMap.put("orderNumber", order.getOrderSn());
                        paramsMap.put("productName", String.join("、", productNames));
                        paramsMap.put("confirmReceiptTime", DateUtil.getCurrentCustomizeFormatDate("yyyy年MM月dd日 HH:mm"));
                        String path = null;
                        // 符合用药周期的才有path路径
                        Long recomId = orderItems.get(0).getRecomId();
                        Boolean enabledDrugremind =
                                OrderJobServiceImpl.this.recommendFeignClient.getEnabledDrugremind(recomId);
                        if (enabledDrugremind) {
                            path = "pages/drugClock/index?recomId=" + recomId;
                            paramsMap.put("tips", "您的药品已签收，请根据医嘱设置用药时间");
                        } else {
                            paramsMap.put("tips", "您的药品已签署，请根据医嘱按时服药");
                        }
                        patientFeignClient.publishMaMessage(MsgMaPublishParamBO.builder()
                                .doctorId(orderItems.get(0).getDoctorId())
                                .patientId(order.getUserId())
                                .msgType(MsgMaPublishParamBO.MsgType.RECEIPT_REMINDER)
                                .content(JSONUtil.toJsonStr(paramsMap))
                                .path(path)
                                .build());

                    }
                    // 保留物流信息
                    List<LogisticsTraceLogBO> logisticsInfoTraceLogs = logisticsInfo.getTraceLogs();

                    //重新保存
                    String orderSn = order.getOrderSn();
                    logisticsInfoTraceLogs.sort(Comparator.comparingLong(o -> o.getTime().getTime()));
                    List<LogisticsRouteOrder> logisticsRouteOrderList = logisticsInfoTraceLogs.stream()
                            .map(m -> LogisticsRouteOrder.builder()
                                    .orderId(orderId)
                                    .orderSn(orderSn)
                                    .content(m.getContent())
                                    .createdAt(m.getTime())
                                    .createdBy("system")
                                    .changedAt(new Date())
                                    .changedBy("system")
                                    .build())
                            .collect(Collectors.toList());
                    if (CollectionUtil.isNotEmpty(logisticsRouteOrderList)) {
                        //删除所有的
                        OrderJobServiceImpl.this.logisticsRouteOrderRepository.removeAllByOrderId(orderId);
                        OrderJobServiceImpl.this.logisticsRouteOrderRepository.saveAll(logisticsRouteOrderList);
                    }
                } catch (Exception e) {
                    log.error("获取快递失败 orderId:{}", orderId, e);
                    transactionStatus.setRollbackOnly();
                }
            }
        }));
    }

    private List<StockQuantityBO> buildStockQuantity(Long orderId) {
        List<OrderItem> orderItemList = this.orderItemRepository.findAllByOrderId(orderId);
        return orderItemList.stream()
                .map(cartItem -> new StockQuantityBO(cartItem.getSkuId(), cartItem.getQuantity()))
                .collect(Collectors.toList());
    }

    private ILogisticsTraceService getLogisticsTraceService(String logisticsCompanyCode) {
        if (!SpringContextHolder.containsBean(logisticsCompanyCode)) {
            return SpringContextHolder.getBean("kudi100", ILogisticsTraceService.class);
        }
        return SpringContextHolder.getBean(logisticsCompanyCode, ILogisticsTraceService.class);
    }

}
